package com.zxl.openapi.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.zxl.openapi.constans.SystemParameter;
import com.zxl.openapi.feign.RedisService;
import com.zxl.openapi.utils.Md5Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Map;
import java.util.TreeMap;

@Component
public class SignFilter extends ZuulFilter {

    private Logger logger = LoggerFactory.getLogger(SignFilter.class);

    @Autowired
    private RedisService redisService;

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 10;
    }

    @Override
    public boolean shouldFilter() {

        Map<String, Map<Object, Object>> allApiInfoMap = SystemParameter.allApiInfoMap;

        RequestContext context = RequestContext.getCurrentContext();

        HttpServletRequest request = context.getRequest();

        String method = request.getParameter("method");
        //获取当前接口的map
        Map<Object, Object> map = allApiInfoMap.get(SystemParameter.REDIS_APIPRE + method);
        //获取当前过滤器需要经过的filters
        String filters = (String) map.get("filters");
        return context.sendZuulResponse() && filters != null && filters.contains(getClass().getName());
    }

    @Override
    public Object run() throws ZuulException {
        //校验签名
        //按照我们的规则来进行校验
        //规则, 客户端将传递的参数按照顺序排序后拼接上它的key,然后生成md5值,然后将md5值作为请求参数在一起传过来,(注意key不能传递过来,因为我们服务起是知道的)
        //服务器规则 获取到请求参数后,去掉sign数据,然后将剩下的内容按照规则排序后拼接上它对应的key,然后生成md5值,再和用户传递的md5值进行比较
        //key如何获取,我们服务器不可能使用同一个key,所以一个用户一个,所以我们需要知道当前的用户是谁
        //如何知道当前的用户是谁,我们要求用户的参数中必须说明它是谁,我们要求用户通过app_key传递
        //我们规定请求中签名参数的name叫sign
        //先获取用户传递的签名,用于比较用的
        RequestContext currentContext = RequestContext.getCurrentContext();

        HttpServletRequest request = currentContext.getRequest();

        String sign = request.getParameter("sign");

        //获取所有的参数名
        Enumeration<String> parameterNames = request.getParameterNames();
        //将参数和对应的值放入到有序map中
        TreeMap<String,String> map = new TreeMap<>();
        while (parameterNames.hasMoreElements()){
            String name = parameterNames.nextElement();
            if(name.equalsIgnoreCase("sign")){
                continue;
            }
            map.put(name,request.getParameter(name));
        }
        //签名还需要当前应用的密码
        //我们必须知道当前的应用是谁,我们规定请求参数中必须有一个appkey这个参数,它的值就是应用的id
        //我们必须知道密码放在哪里,我们可以亿应用id为key放入到redis中
        //在redis中应用信息到底是什么结构的数据 string? hash? set? zset? list? geo?
        //我们分析了一下 一个应用的信息可能会有好多个不同的字段,只不过我们现在可能已知的有密码,名字,所以我们可以使用hash
        //我们规定 密码在redis中的属性名为appsecret
        //对传递的参数进行签名
        //判断用户传递的签名和我们算出来的签名是不一致
        String appkey = request.getParameter("appkey");

        Map<Object, Object> appInfoMap = redisService.hgetAll(SystemParameter.REDIS_APIKEYPRE + appkey);

        String appsecret = (String) appInfoMap.get("appsecret");

        String signature = Md5Util.md5Signature(map, appsecret);

        logger.error("算出来的签名是：" + signature);

        if(!sign.equalsIgnoreCase(signature)){

            currentContext.setSendZuulResponse(false);

            currentContext.getResponse().setContentType("text/html;charset=utf-8");

            currentContext.setResponseBody("签名校验失败");

        }else {

            redisService.set(signature,"xixixixixixixixixi",60000);

        }

        return null;

    }
}
